package com.kingmed.kmss.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.kingmed.kmss.framework.feign.client.admin.RemoteAdminService;
import com.kingmed.kmss.framework.feign.client.auth.RemoteOAService;
import com.kingmed.kmss.auth.service.AuthService;
import com.kingmed.kmss.common.dto.admin.ums.AdminAuthDTO;
import com.kingmed.kmss.common.dto.admin.ums.AdminLoginDTO;
import com.kingmed.kmss.common.dto.admin.ums.LoginUserDTO;
import com.kingmed.kmss.common.dto.admin.ums.OACheckDTO;
import com.kingmed.kmss.common.enums.admin.UserSource;
import com.kingmed.kmss.common.exception.BusinessException;
import com.kingmed.kmss.common.satoken.LoginHelper;
import com.kingmed.kmss.common.utils.BeanCopyUtils;
import com.kingmed.kmss.common.vo.CommonResponse;
import com.kingmed.kmss.framework.cpwx.service.KmWxCpOAuth2Service;
import com.kingmed.kmss.framework.feign.client.admin.CommonAdminFeignServiceFacade;
import feign.Response;
import feign.Util;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.bean.WxCpOauth2UserInfo;
import org.apache.http.util.Asserts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.util.Collection;
import java.util.regex.Pattern;

@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    // OA登录验证是否成功（验证字符串）
    private static final String TGT_CREATED_FLG = "Created";
    // 获取到的OA TGT信息
    private static final String OA_LOGIN_RES_LOCATION = "location";
    // TGT信息url的前缀，用于校验
    private static final String OA_LOGIN_TGT_PREFIX = "/sso/v1/tickets/TGT";

    @Value("${oa.mgr.service}")
    private String SERVICE;
    @Value("${oa.mgr.redirectUrl}")
    private String REDIRECT_URL;
    @Value("${oa.url}")
    private String OA_VALIDATE_URL;
    @Value("${oa.sales.service}")
    private String SALES_SERVICE;// 移动端service
    @Value("${oa.sales.redirectUrl}")
    private String SALES_REDIRECT_URL;// 移动端重定向url

    @Autowired
    private RemoteAdminService remoteAdminService;
    @Autowired
    private RemoteOAService remoteOAService;
    @Autowired
    private KmWxCpOAuth2Service kmWxCpOAuth2Service;

    @Override
    public String login(AdminLoginDTO adminLoginDto) {
        try {
            // 查询账号信息
            CommonResponse<AdminAuthDTO> oaAdminRes = remoteAdminService.loadAdminAuthDtoByUsernameNew(adminLoginDto);
            if (oaAdminRes == null || oaAdminRes.getData() == null)
                return null;
            log.info("AuthServiceImpl_login_userRes：" + oaAdminRes);
            String userSource = oaAdminRes.getData().getUserSource();
            if (UserSource.CRM_USER.getUserType().equals(userSource)) {
                // CRM登录
                LoginUserDTO loginUser = this.handleResult(remoteAdminService.loginCheck(adminLoginDto));
                if (loginUser != null) {
                    return loginUser.getToken();
                }
            } else if (UserSource.OA_USER.getUserType().equals(userSource)) {
                // crm账户反向登录OA系统验证
                return this.oaLoginCheck(adminLoginDto);
            }
            return null;
        } catch (Exception e) {
            log.error("AuthServiceImpl登录异常：", e);
            throw new BusinessException("登录异常");
        }
    }

    /**
     * crm账户反向登录OA系统验证
     */
    private String oaLoginCheck(AdminLoginDTO adminLoginDto) {
        OACheckDTO oaCheckDTO = new OACheckDTO();
        oaCheckDTO.setUsername(adminLoginDto.getUsername());
        oaCheckDTO.setPassword(adminLoginDto.getPassword());
        try {
            Response response = remoteOAService.oaLoginTickets(oaCheckDTO);
            log.info("OA反向登录校验结果：" + response);

            if (response != null && response.status() == HttpStatus.HTTP_CREATED && TGT_CREATED_FLG.equals(response.reason())) {
                Collection<String> locations = response.headers().get(OA_LOGIN_RES_LOCATION);
                String tgtStr = locations.stream().findFirst().isPresent() ? locations.stream().findFirst().get() : StrUtil.EMPTY;

                if (tgtStr.startsWith(OA_VALIDATE_URL.concat(OA_LOGIN_TGT_PREFIX))) {
                    log.info("OA登录成功TGT：" + tgtStr);
                    LoginUserDTO loginUser = this.handleResult(remoteAdminService.oaLoginQuery(adminLoginDto.getUsername()));
                    return loginUser != null ? loginUser.getToken() : null;
                }
            }
        } catch (Exception e) {
            log.error("调用OA验证登录异常：", e);
            throw new BusinessException("调用OA验证登录异常");
        }
        return null;
    }

    /**
     * 处理登录返回用户数据
     */
    private LoginUserDTO handleResult(CommonResponse<AdminAuthDTO> result) {
        if (result == null) {
            return null;
        }
        if (result.getCode() == HttpStatus.HTTP_UNAUTHORIZED || BeanUtil.isEmpty(result.getData())) {
            return null;
        }
        AdminAuthDTO adminAuthDto = result.getData();
        //adminAuthDto为null表示login失败
        LoginUserDTO loginUser = new LoginUserDTO();
        //深拷贝
        BeanCopyUtils.copy(adminAuthDto, loginUser);

        LoginHelper.login(loginUser);
        return loginUser;
    }


    /**
     * 处理登录返回用户数据
     */
    private LoginUserDTO handleResult(CommonResponse<AdminAuthDTO> result, long timeoutSeconds, String device) {
        if (result == null) {
            return null;
        }
        if (result.getCode() == HttpStatus.HTTP_UNAUTHORIZED || BeanUtil.isEmpty(result.getData())) {
            return null;
        }
        AdminAuthDTO adminAuthDto = result.getData();
        //adminAuthDto为null表示login失败
        LoginUserDTO loginUser = new LoginUserDTO();
        //深拷贝
        BeanCopyUtils.copy(adminAuthDto, loginUser);

        LoginHelper.loginWithTimeout(loginUser, timeoutSeconds, device);
        return loginUser;
    }

    /**
     * oa单点登录
     * 1. OA应⽤中⼼点击应⽤，验证当前⽤户登录有效，登录有效则获取⽤户ticket信息跳转service地址
     * 2. 重定向到CRM的auth/oa/login接⼝、并传⼊ticket令牌
     * 3. 调⽤OA接口，ticket做鉴权
     * 4. 鉴权成功，初始化CRM系统⽤户token、加载userInfo并redirect到系统⾸⻚
     *
     * @return CRM重定向URL带参数
     */
    @Override
    public String oaLogin(String ticket) {
        OACheckDTO oaCheckDTO = new OACheckDTO();
        oaCheckDTO.setTicket(ticket);
        oaCheckDTO.setService(SERVICE);
        log.info("OA鉴权验证ticket：" + ticket);
        try {
            Response response = remoteOAService.proxyValidate(oaCheckDTO);
            String loginId = this.handleOAResponse(response);
            log.info("OA验证ticket结果loginId：" + loginId);
            LoginUserDTO loginUser = this.handleResult(remoteAdminService.oaLoginQuery(loginId));
            if(ObjectUtil.isNull(loginUser)){
                log.info("该员工号不存在:{}", loginId);
                throw new BusinessException("该员工号不存在");
            }
            // CRM前端重定向URL带参数
            return REDIRECT_URL + "?username=" + loginUser.getUsername() + "&token=" + loginUser.getToken();
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("OA验证ticket结果异常：", e);
            throw new BusinessException("OA单点登录异常");
        }
    }

    /**
     * 移动端app的单点登
     * 1、移动端点击应用拿到ticket，验证当前用户是否有效，登录有效则获取⽤户ticket信息跳转service地址
     * 2、重定向到CRM的auth/oa/sales/login接⼝、并传⼊ticket令牌
     * 3、调⽤OA接口，ticket做鉴权
     * 4、鉴权成功，初始化CRM系统⽤户token、加载userInfo并redirect到系统⾸⻚
     *
     * @param ticket
     * @return
     */
    @Override
    public String oaSalesLogin(String ticket) {
        OACheckDTO oaCheckDTO = new OACheckDTO();
        oaCheckDTO.setTicket(ticket);
        oaCheckDTO.setService(SALES_SERVICE);
        log.info("OA鉴权验证ticket：" + ticket);
        try {
            // oa验证ticket是否有效地址
            Response response = remoteOAService.proxyValidate(oaCheckDTO);
            String loginId = this.handleOAResponse(response);
            log.info("OA验证ticket结果loginId：" + loginId);
            LoginUserDTO loginUser = this.handleResult(remoteAdminService.oaLoginQuery(loginId));
            // kmss-业务员移动端重定向URL带参数
            String redirectUrl = SALES_REDIRECT_URL + "?username=" + loginUser.getUsername() + "&token=" + loginUser.getToken();
            log.info("redirectUrl：" + redirectUrl);
            return redirectUrl;
        } catch (Exception e) {
            log.info("OA验证ticket结果异常：", e);
            throw new BusinessException("OA单点登录异常");
        }
    }

    @Override
    public String unAuthLogin(String username) {
        CommonResponse<AdminAuthDTO> adminAuthDTOResp = remoteAdminService.loadAdminAuthDtoByUsernameNew(new AdminLoginDTO(username, "", ""));
        if (adminAuthDTOResp == null || adminAuthDTOResp.getData() == null) {
            throw new BusinessException("用户名异常");
        }
        // 登录，10分钟过期,目前仅企微场景调用
        LoginUserDTO loginUser = this.handleResult(adminAuthDTOResp, 10L * 60L, "cpwx");
        Asserts.notNull(loginUser, "登录失败");
        return loginUser.getToken();
    }

    @Override
    public String cpwxPcLogin(String code, Integer agentId) {
        try {
            WxCpOauth2UserInfo authUserInfo = kmWxCpOAuth2Service.getUserInfo(agentId, code);
            log.info("获取用户信息：{}", authUserInfo);
            LoginUserDTO loginUser = this.handleResult(remoteAdminService.oaLoginQuery(authUserInfo.getUserId()));
            String redirectUrl = REDIRECT_URL + "?username=" + loginUser.getUsername() + "&token=" + loginUser.getToken();
            log.info("Pc企微认证redirectUrl：" + redirectUrl);
            return redirectUrl;
        } catch (Exception e) {
            throw new BusinessException("企微认证失败！", e);
        }
    }

    @Override
    public String cpwxClientLogin(String code, Integer agentId) {
        try {
            WxCpOauth2UserInfo authUserInfo = kmWxCpOAuth2Service.getUserInfo(agentId, code);
            log.info("获取用户信息：{}", authUserInfo);
            LoginUserDTO loginUser = this.handleResult(remoteAdminService.oaLoginQuery(authUserInfo.getUserId()));
            String redirectUrl = SALES_REDIRECT_URL + "?username=" + loginUser.getUsername() + "&token=" + loginUser.getToken();
            log.info("Pc企微认证redirectUrl：" + redirectUrl);
            return redirectUrl;
        } catch (Exception e) {
            throw new BusinessException("企微认证失败！", e);
        }
    }

    /**
     * 解析OA返回的xml数据
     */
    private String handleOAResponse(Response response) throws Exception {
        String loginId = StrUtil.EMPTY;
        String xmlString = Util.toString(response.body().asReader(Util.UTF_8));
        log.info("OA验证ticket接口结果：" + xmlString);
        if (xmlString == null || xmlString.equals(StrUtil.EMPTY)) {
            throw new BusinessException("OA登录接口结果为空");
        }
        xmlString = Pattern.compile("\r|\n|cas:|:cas").matcher(xmlString).replaceAll(StrUtil.EMPTY);
        InputSource is = new InputSource(new StringReader(xmlString));
        Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
        NodeList loginNodes = doc.getElementsByTagName("loginid");
        if (loginNodes != null) {
            Node loginNode = loginNodes.item(0);
            loginId = loginNode != null ? loginNode.getTextContent() : "";
        }
        if (StrUtil.isEmpty(loginId)) {
            throw new BusinessException("OA验证ticket结果无loginId");
        }
        return loginId;
    }

}
